package com.lanxin.judge.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lanxin.judge.Vo.NodeVo;
import com.lanxin.judge.entity.LcNode;
import com.lanxin.judge.mapper.LcNodeMapper;
import com.lanxin.judge.service.ILcNodeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.xml.soap.Node;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author author
 * @since 2025-08-04
 */
@Service
public class LcNodeServiceImpl extends ServiceImpl<LcNodeMapper, LcNode> implements ILcNodeService {

    @Autowired
    private LcNodeMapper nodeMapper;

    //获取流程的审批节点VO,排序后的节点列表
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!不许动这个代码！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！
    @Override
    public List<NodeVo> listNode(Integer flowId) {
        //排序前的集合
        List<NodeVo> nodeVos = nodeMapper.listNodeVo(flowId);
        //排序后的集合,arrayList集合维护输入输出顺序
        List<NodeVo> nodeVos2 = new ArrayList<NodeVo>();
        //头节点
        NodeVo head = null;
        //将查询出的审批节点进行排序，得到正确的审批顺序
        for (NodeVo nodeVo : nodeVos) {
            if(nodeVo.getPrenodeid()==null){
                //得到头结点
                head = nodeVo;
                nodeVos2.add(head);
                break;
            }
        }
        //如果没有头节点，直接返回
        if(head==null){
            return nodeVos2;
        }
        //找到头结点后对剩下的节点进行排序
        NodeVo current = head;
        while (nodeVos2.size()<nodeVos.size()) {
            boolean fundNext = false;//循环标识，防止死循环
            for(NodeVo nodeVo : nodeVos){
                //从头结点顺着往下推出正确顺序
                if(nodeVo.getPrenodeid()!=null&&nodeVo.getPrenodeid().equals(current.getOid())
                &&!nodeVos2.contains(nodeVo)){
                    //更新当前结点
                    current = nodeVo;
                    nodeVos2.add(nodeVo);
                    fundNext = true;//更新标识
                }
            }
            if(!fundNext){
                break;//如果链表断裂或者循环引用则结束循环
            }
        }
        return nodeVos2;
    }

    //删除节点!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!不许动这个代码！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！
    @Override
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.SERIALIZABLE,rollbackFor = Exception.class)
    //oid为节点id，flowId为流程id
    public void deleteNode(Integer oid,Integer flowId) {
        try {
            //删除前的节点列表
            List<NodeVo> oldNodes= listNode(flowId);
            //得到要删除的是第几个节点
            int target = -1;
            for(int i=0;i<oldNodes.size();i++){
                if(oldNodes.get(i).getOid().equals(oid)){
                    target = i;
                    break;
                }
            }
            //未找到节点直接返回
            if(target==-1){
                return ;
            }
            NodeVo preNode = null;
            //得到目标节点的前项节点
            if(target-1>=0){
                preNode = oldNodes.get(target-1);
            }
            //拿到目标节点的下一个节点
            NodeVo nextNode = null;
            if(target+1<oldNodes.size()){
                nextNode = oldNodes.get(target+1);
            }
            //如果删除的是最后一个节点则无需修改链表，直接返回
            if(nextNode==null){
                nodeMapper.deleteById(oid);
                return ;
            }
            //拿到第二个节点
            NodeVo second = (oldNodes.size() >= 2) ? oldNodes.get(1) : null;
            //删除节点
            //将被删节点的后项节点删除，否则会因为外键而无法删除
            updatePreNode(nextNode.getOid(),null);
            int row = nodeMapper.deleteById(oid);
            //删除后的流程的剩余节点
            List<NodeVo> nodeVos2 = listNode(flowId);
            //如果删除后链表为空则直接返回
            if(nodeVos2.isEmpty()){
                return;
            }
            if(row>0){
                //如果删除的是头结点则需要修改第二个节点为头结点
                if(target==0&&second!=null){
                    updatePreNode(second.getOid(),null);
                }else {
                    //如果删除的不是头结点,则修改目标节点的下一个节点连接到目标节点的前一个节点
                    updatePreNode(nextNode.getOid(),preNode.getOid().toString());
                    //如果删除节点后只剩一个节点，将剩余的节点设置为头结点
                    if(nodeVos2.size()==1){
                        int head = nodeVos2.get(0).getOid();
                        //将其上级节点设置为空
                        updatePreNode(head,null);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("删除节点失败");
        }
    }

    //添加节点!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!不许动这个代码！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class,isolation = Isolation.SERIALIZABLE)
    public void addNode(int oid,LcNode node) {
        try {
            //设置节点归属哪个流程
            node.setParentid(oid);
            //得到当前流程的原来节点列表
            List<NodeVo> nodes = listNode(node.getParentid());
            //如果当前流程还没有节点则直接将节点插入
            if(nodes.isEmpty()){
                nodeMapper.insert(node);
                return;
            }
//-------------------------------------------------------------------------------
            //若需要将节点插入头部，即node.prenodeid为null
            if(node.getPrenodeid()==null){
                //1.向表中插入该节点
                int row = nodeMapper.insert(node);
                //2.将原头节点的上级节点设置为新的头节点
                if(row>0){
                    LcNode head = null;
                    //得到原来的头结点
                    for (LcNode n : nodes) {
                        if(n.getPrenodeid()==null){
                            head = n;
                        }
                    }
                    //修改原来的头结点的上级节点,int oid,int preNodeId
                    updatePreNode(head.getOid(),node.getOid().toString());
                }
                return;
            }
//-------------------------------------------------------------
            //向流程节点中插入节点，插入位置是上级节点的后一个
            //即上级节点A,插入节点B,原上级节点的下一个节点C,插入后顺序应是A->B->C
            //1.遍历得到插入节点的上级节点
            int targetIndex = -1;
            for (int i=0;i<nodes.size();i++) {
                if(nodes.get(i).getOid().equals(node.getPrenodeid())){
                    targetIndex = i;
                }
            }
            //2.将节点插入数据库
            nodeMapper.insert(node);
            //3.如果目标节点的下标是列表的最后一位则无需修改
            //如果目标节点的下标不是最后一位则需要修改原来连接在目标节点后的节点的上级节点id，即c的上级节点id
            if(targetIndex+1<nodes.size()){
                // 得到目标节点的后一个节点
                LcNode after = nodes.get(targetIndex+1);
                //修改上级节点
                updatePreNode(after.getOid(),node.getOid().toString());
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("添加节点失败");
        }
    }

    //修改节点的上级节点
    @Override
    public int updatePreNode(Integer oid, String preNodeId) {
        HashMap<String,Object> map = new HashMap<>();
        map.put("oid",oid);
        map.put("preNodeId",preNodeId);
        return nodeMapper.updatePreNode(map);
    }

    //修改节点!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!不许动这个代码！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！
    @Override
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.SERIALIZABLE,rollbackFor = Exception.class)
    public void updateNode(LcNode node) {
        try {
            //流程id
            int flowId=node.getParentid();
            //得到修改前的节点列表
            List<NodeVo> nodeVos = listNode(flowId);
            NodeVo target = null;//目标节点
            int targetIndex = -1;
            NodeVo preNode = null;//前项节点
            NodeVo tail = nodeVos.get(nodeVos.size()-1);//尾节点
            NodeVo nextNode = null;//后项节点
            int index2 = -1;//新的上级节点的位置
            for(int i=0;i<nodeVos.size();i++){
                if(nodeVos.get(i).getOid().equals(node.getPrenodeid())){
                    index2 = i;
                }
            }
            //遍历得到目标节点
            for (int i=0;i<nodeVos.size();i++) {
                if(nodeVos.get(i).getOid().equals(node.getOid())){
                    target = nodeVos.get(i);
                    targetIndex = i;
                }
            }
            NodeVo head = nodeVos.get(0);
            if(target==null){
                return ;
            }
            if(targetIndex>=1){
                preNode = nodeVos.get(targetIndex-1);
            }
            if(targetIndex+1<nodeVos.size()){
                nextNode = nodeVos.get(targetIndex+1);
            }
            //更新节点信息
            int row = nodeMapper.updateById(node);
            if(row<=0){
                return ;
            }
            //判断是否更新了上级节点
            if(target.getPrenodeid()!=node.getPrenodeid()){
                //如果更新了上级节点则需要对其他节点做出修改
                //-----如果设置为头结点，需要将原头节点的上级节点设置为更新节点,将更新节点的后一个节点的上级节点设置为更新节点的上级节点
                if(node.getPrenodeid()==null&&!node.getOid().equals(head.getOid())){//只有当更新节点不是原来的头结点时才会更新,避免了当更新头结点信息时重复执行代码
                    //如果只有一个节点，那么这个节点一定是头结点，无需修改，所以preNode一定不为空
                    updatePreNode(head.getOid(),node.getOid().toString());//将原头节点的上级节点设置为新的头结点
                    if(nextNode==null){
                        //当更新的是最后一个节点时需要修改原来的头结点，并修改目标节点的前项节点为空
                        updatePreNode(node.getOid(),null);
                        return;
                    }
                    //如果不是最后一个节点则需要将后一个节点的前项指针指向更新节点的上一个节点
                    updatePreNode(nextNode.getOid(),preNode.getOid().toString());
                } else if (node.getOid().equals(head.getOid())) {//如果更新的是原头节点，
                    if(node.getPrenodeid().equals(tail.getOid())){
                        //如果更新头节点的上级节点为最后一个节点，即1-2变为2-1，只需设置新的头节点2
                        updatePreNode(nextNode.getOid(),null);
                        return;
                    }
                    //如果前项节点不是尾节点,即1-2-3变为2-1-3，将1的上级变为2
                    updatePreNode(nextNode.getOid(),null);//设置新的头节点
                    updatePreNode(nodeVos.get(index2+1).getOid(),node.getOid().toString());//将新的上级节点的后项节点的上级节点设置为node.oid
                } else if (node.getPrenodeid()==null&&node.getOid().equals(head.getOid())) {
                    //如果更新的是头节点则无需修改
                    return ;
                }else {
                    //如果既没有更新节点为头节点，也不是更新头结点，只是普通的变换节点的上级节点
                    if(targetIndex>=nodeVos.size()){
                        /*如果更新的是尾节点，如1->2->3变为1->3->2，只需要将目标节点3的新的上级节点1的后项节点2的前项指针指向目标节点3即可,
                        如果链表只有1->2两个节点，那么就变成了将2设置为头结点，那么前面的逻辑判断就会拦截
                        */
                        updatePreNode(nodeVos.get(index2+1).getOid(),node.getOid().toString());
                        return;
                    }
                    /*更新的是中间节点,例如1—>2->3->4变为1->3->2->4,即将3的上级节点变为1，传入的node的上级节点是已经修改过的即已经修改为1了的，
                        此时的状态是1的下级既有2又有3即1->2,3->4,现需要将目标节点的后项节点的前项节点更新为目标节点的前项节点，同时将
                        node.getPreNodeId的节点的后项节点的前项节点更新为目标节点
                    * */

                    if (index2+1<nodeVos.size()) {//若不是将最后一个节点作为新的上级节点
                        updatePreNode(nodeVos.get(index2+1).getOid(),node.getOid().toString());//将node.getPreNodeId的节点的后项节点的前项节点更新为目标节点
                    }
                    updatePreNode(nextNode.getOid(),preNode.getOid().toString());//将目标节点的后项节点的前项节点更新为目标节点的前项节点
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("修改节点失败");
        }
    }

}
